/**
 *
 * 如果 then 方法返回的是一个promise，那么就执行这个promise的then方法。
 * then方法接受一个回调函数。但是then方法从属一个promise。这个promise fulfilled 之后才会对data
 * 调用这个回调，并且返回一个新的promise，新的promise的值就是回调计算出的值。那么如果这个promise
 * 是pending状态，那么then方法，就会返回一个同样是pending状态的promise2. 并且对自己从属的promise
 * 的handleque中进行注册一个函数、。这个函数是接受当前promise的data作为参数，调用then中的接受的handle
 * 然后计算出新的data2，然后使用promise2来resolve这个data2. 其实走到这里就是一个类似递归的东西
 * @export
 * @class PromiseP
 * @template T
 */
export default class PromiseP<T> {
    data: any;

    status: 'pending' | 'fulfilled' | 'rejected' = 'pending';

    handlesQue: ((data: T) => any)[] = [];

    constructor(producer?: (resolve: Function, reject: Function) => any) {
        producer && producer(this.resolveFn, this.rejectFn);
    }

    resolveFn = (data: T) => {
        this.data = data;
        this.status = 'fulfilled';
        if (this.handlesQue && this.handlesQue.length) {
            this.handlesQue.forEach(h => {
                this.excute(h, this.data);
            });
            this.clearHandlesQue();
        }
        return this;
    }

    rejectFn = (data: any) => {
        this.data = data;
        this.status = 'rejected';
        return this;
    }

    then(handle: (data: T) => any) {
        if (this.status === 'fulfilled' || this.status === 'rejected') {
            return this.excute(handle, this.data);
        }
        const p = this.resolvePromise(handle);
        return p;
    }

    resolvePromise(handle: (data: T) => any) {
        const p = new PromiseP();
        p.status = 'pending';
        const realHandle = () => {
            const result = handle(this.data);
            if (result.then) {
                result.then();
            }
            p.resolveFn(result);
            return p;
        }
        // 这个 p 的resolve后的回调也需要注入到当前promise的handleque中
        this.handlesQue.push(realHandle);
        return p;
    }

    excute(handle: (data: any) => any, data: any) {
        try {
            const result = handle(data);
            const p = new PromiseP();
            p.data = result;
            p.status = 'fulfilled';
            return p;
        } catch (error) {
            const p = new PromiseP();
            p.data = error;
            p.status = 'rejected';
            return p;
        }
    }
    clearHandlesQue() {
        this.handlesQue = [];
    }
}